package com.pi.web.controller;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import com.pi.framework.core.exception.ApplicationException;
import com.pi.framework.core.security.AuthenticationRequest;
import com.pi.framework.core.security.JwtTokenUtil;
import com.pi.sys.entity.Menu;
import com.pi.sys.entity.User;
import com.pi.sys.service.MenuService;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@RestController
@Slf4j
public class AuthenticationRestController {
	@Data
	class Token {
		private String username;
		private boolean admin = false;
		private String error;
		private String token;
		private List<String> menus;

		public Token(String username, String token, List<String> menus) {
			this.username = username;
			this.token = token;
			this.menus = menus;
		}

		public Token() {
			// TODO Auto-generated constructor stub
		}
	}

	@Value("${security.jwt.header}")
	private String tokenHeader;

	@Autowired
	private AuthenticationManager authenticationManager;

	@Autowired
	private JwtTokenUtil jwtTokenUtil;

	// @Autowired
	// private UserDetailsService userDetailsService;

	@Autowired
	private MenuService menuService;

	@ExceptionHandler
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public @ResponseBody Token exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception e) {
		log.error(e.getLocalizedMessage());
		Token token = new Token();
		if (e instanceof ApplicationException) {
			token.setError(e.getLocalizedMessage());
			return token;
		} else {
			token.setError(e.getLocalizedMessage());
			return token;
		}
	}

	@RequestMapping(value = "${security.jwt.route.authentication.login:/auth}", method = RequestMethod.POST)
	public Token login(@RequestBody AuthenticationRequest authenticationRequest) throws AuthenticationException {
		log.info("user {} tring to log in.", authenticationRequest.getUsername());

		// Perform the security
		String username = authenticationRequest.getUsername();
		Authentication authentication;

		authentication = authenticationManager
				.authenticate(new UsernamePasswordAuthenticationToken(username, authenticationRequest.getPassword()));
		SecurityContextHolder.getContext().setAuthentication(authentication);

		// Reload password post-security so we can generate token
		// final UserDetails userDetails =
		// userDetailsService.loadUserByUsername(username);
		// Long userId = SecurityUtils.getLoginUserId();
		// 检查是否从其他终端已经登陆
		final String token = jwtTokenUtil.generateToken(username.toLowerCase());
		Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
		Object principal = authentication.getPrincipal();
		boolean admin = (principal instanceof User) ? ((User) principal).isAdmin() : false;
		List<String> roleNames = new ArrayList<String>();
		authorities.forEach(ga -> roleNames.add(ga.getAuthority()));
		List<Menu> grantedMenus = new ArrayList<>();
		grantedMenus = menuService.findByRoleNames(roleNames);
		List<String> menus = new ArrayList<>();
		grantedMenus.forEach(menu -> menus.add(menu.getName()));
		// Return the token
		Token ret = new Token(username, token, menus);
		ret.setAdmin(admin);

		return ret;

	}

	@RequestMapping(value = "${security.jwt.route.authentication.logout}", method = RequestMethod.GET)
	public Token logout(HttpServletRequest request) {
		String token = request.getHeader(tokenHeader);
		String username = jwtTokenUtil.getUsernameFromToken(token);
		if (username == null || username == "") {
			username = request.getParameter("username");
		}
		// jwtTokenUtil.validateToken(token, userDetails);
		// JwtUser user = (JwtUser) userDetailsService.loadUserByUsername(username);
		return new Token();
	}

	@RequestMapping(value = "${security.jwt.route.authentication.refresh}", method = RequestMethod.GET)
	public Token refreshAndGetAuthenticationToken(HttpServletRequest request) {
		String token = request.getHeader(tokenHeader);
		String username = jwtTokenUtil.getUsernameFromToken(token);
		// JwtUser user = (JwtUser) userDetailsService.loadUserByUsername(username);
		if (jwtTokenUtil.canTokenBeRefreshed(token)) {
			String refreshedToken = jwtTokenUtil.refreshToken(token);
			return new Token(username, refreshedToken, null);
		} else {
			return new Token();
		}
	}

}